package gnss_plugs

import (
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"io"
	"math"
	"os"
	"strings"
	"sync/atomic"
	"time"
)

type GnssRinexObsRtWriter struct {
	Id             string
	MountPoint     string
	Sys            byte
	bodyCnt        int
	curTmpFileName string
	lastFileName   string
	Filefmt        string
	tmpPath        string
	SignIsOK       bool
	AutowireSign   bool // 自动更新sign
	signHeadstr    string
	signupflag     bool
	rinexHeader    *gnss.RinexHeader
	signstartT     time.Time
	startTime      time.Time
	currTime       time.Time // 当前时间的观测值
	FixInterval    int64     // 固定采样间隔 单位：秒  5s,  mod(t, 5) = 0

	obsWriter io.Writer
	ephWriter io.Writer

	// 存放一个历元的观测值
	obsList *gnss.EpochRinexObs

	// 检测系统重入bug
	lastgid          uint64
	checkworkingflag int32
	lastdebuginfo    string
	DebuginfoFunc    func() string
}

func NewGnssRinexObsRtWriter() *GnssRinexObsRtWriter {
	rval := &GnssRinexObsRtWriter{
		Id:          utils.RandKeyString(4),
		Filefmt:     "",
		currTime:    time.Time{},
		obsList:     gnss.NewEpochRinexObs(),
		rinexHeader: gnss.NewRinexHeader(),
	}
	return rval
}

func (this *GnssRinexObsRtWriter) checkNewFile(curT time.Time) bool {
	str := this.Filefmt
	str = utils.ParseTimeFmt(str, curT)
	return strings.Compare(this.lastFileName, str) != 0
}

func (this *GnssRinexObsRtWriter) checkFileStart() {
	if len(this.curTmpFileName) == 0 {
		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}
		strFileN := utils.ParseTimeFmt(this.Filefmt, this.startTime)
		this.lastFileName = strFileN
		this.curTmpFileName = TempFilesDirectory + utils.PathSeparator + "~obs." + utils.RandKeyString(5) + ".tmp"
		utils.ForceCreateFilePath(this.curTmpFileName)
		utils.ForceCreateFilePath(this.lastFileName)
	}
}

func (this *GnssRinexObsRtWriter) checkFileEnd() {
	if len(this.curTmpFileName) == 0 {
		return
	}

	// first line
	str := gnss.PrintRinexFV(3.03, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
	str = utils.AddSuffixForWidth(str, 60, " ")
	str += "RINEX VERSION / TYPE\n"

	if this.startTime.IsZero() {
		this.startTime = time.Now()
	}

	str += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		utils.AddSuffixForWidth(this.startTime.Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	str += this.signHeadstr
	str += "  " + this.startTime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n"
	str += "  " + this.currTime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF END OBS\n"
	str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"
	strName := utils.ParseTimeFmt(this.Filefmt, this.startTime)
	utils.NewFileData(strName, []byte(str))
	//utils.InsertBeforeLineFile()
	utils.AppendAllFile(strName, this.curTmpFileName)
	os.Remove(this.curTmpFileName)
	this.curTmpFileName = ""
	this.startTime = utils.ZeroTime
	this.bodyCnt = 0
}

func (this *GnssRinexObsRtWriter) Stop() {
	this.checkFileEnd()
}

func (this *GnssRinexObsRtWriter) OnRecvGnssRecCallBack(sender interface{}, channelid string, subscribeid interface{}, data interface{}, tag interface{}, isBreak *bool) bool {
	if atomic.CompareAndSwapInt32(&this.checkworkingflag, 0, 1) {
		this.lastgid = utils.GetCurrentGoRoutineID()
		if this.DebuginfoFunc != nil {
			this.lastdebuginfo = this.DebuginfoFunc()
		}
		defer func() {
			this.checkworkingflag = 0
		}()
		if data == nil {
			this.checkFileEnd()
			return true
		}
		rtcmRec, ok := data.(*gnss.GnssRtcmData)
		if ok {
			this.OnRecvGnssDataRec(sender, rtcmRec)
		}
		return true
	} else {
		lastid := this.lastgid
		curr_gid := utils.GetCurrentGoRoutineID()
		debuginfo := "[需要设定debuginfofunc,进行获取信息]"
		if this.DebuginfoFunc != nil {
			debuginfo = this.DebuginfoFunc()
		}
		str := fmt.Sprintf("[并发重入]%s(%s), current_gid:%d\r\n debuginfo:%s\r\n "+"==============\r\n"+
			"lastgid:%d\r\n lastdebuginfo:%s\r\n", this.Id, this.MountPoint,
			curr_gid, debuginfo,
			lastid, this.lastdebuginfo)
		utils.Log.WarnTypef("GNSS-BUG", str)
		return false
	}

	//if data == nil {
	//	this.checkFileEnd()
	//	return true
	//}
	//rtcmRec, ok := data.(*gnss.GnssRtcmData)
	//if ok {
	//	this.InputRtcmData(sender, rtcmRec)
	//}
	//return true
}

/*
*
 */
func (this *GnssRinexObsRtWriter) checkSignHeadStr() bool {
	if len(this.signHeadstr) == 0 {
		this.signHeadstr = this.rinexHeader.PrintSignHeader()
		return true
	}

	return false
}

/**
 * 添加成功返回true, 已经存在返回false
 */
func (this *GnssRinexObsRtWriter) CheckAddSign(sys byte, signv byte) bool {
	return this.rinexHeader.CheckAddSign(sys, signv)
}

func (this *GnssRinexObsRtWriter) checkSignUpdate(rtcmTime time.Time, msm *gnss.RtcmMsm) bool {
	this.signupflag = false
	if this.SignIsOK {
		return true
	}
	if this.signstartT.IsZero() {
		this.signstartT = rtcmTime
	}

	if this.AutowireSign || utils.SecondOf(this.signstartT, rtcmTime) < 30 {
		h := msm.Header
		up := false
		var k byte
		for k = 0; k < h.SigN; k++ {
			sigi := h.Sigs[k]
			if this.CheckAddSign(msm.Sys, sigi) {
				up = true
			}
		}

		if up {
			this.signupflag = true
			this.signHeadstr = ""
		}
	}

	return utils.SecondOf(this.signstartT, rtcmTime) >= 30
}

/**
 * 写入前一秒的观测值到文件
 */
func (this *GnssRinexObsRtWriter) writePreRinexObsEpoch() {
	if this.bodyCnt == 0 {
		return
	}

	defer func() {
		this.bodyCnt = 0
	}()

	if this.FixInterval > 0 {
		f_sec := float64(this.currTime.UnixNano()) / 1e9 // 秒，有小数
		i_sec := int64(math.Floor(f_sec + 0.5))
		v := i_sec % this.FixInterval
		if v != 0 {
			utils.Log.Debugf("固定采样率为:%d,最后时间:%s", this.FixInterval, utils.DateTimeString(this.currTime))
			return
		}
	}

	n, strRinex := this.obsList.ToRinex3String(this.rinexHeader)
	if n == 0 {
		return
	}

	str := "> " + this.currTime.Format("2006 01 02 15 04 05") + ".0000000  0 " + fmt.Sprintf("%.2d", n)
	str += "\n"
	str += strRinex

	if this.checkNewFile(this.currTime) {
		this.checkFileEnd() // 上个文件处理

		this.startTime = this.currTime

		if this.startTime.IsZero() {
			this.startTime = time.Now()
		}

		// 初始化新文件
		this.checkFileStart()
	}

	_, err := utils.AppendFile(this.curTmpFileName, []byte(str))
	if err != nil {
		utils.Log.Errf("写入出现异常:%s, 文件:%s", err.Error(), this.curTmpFileName)
	}

}

func (this *GnssRinexObsRtWriter) OnRecvGnssDataRec(sender interface{}, rec *gnss.GnssRtcmData) {
	//if rec.RtcmTime.IsZero(){
	//	rec.RtcmTime = utils.ZeroTime;
	//}
	if rec.TypeId == gnss.TYPE_OBS {
		if obs, ok := rec.Data.(*gnss.RtcmMsm); ok {

			obs.ProcObs()
			if rec.RtcmTime != this.currTime {
				this.writePreRinexObsEpoch()
				this.obsList.Reset()
				this.currTime = rec.RtcmTime
				this.obsList.AddMsm(obs)
				this.bodyCnt++

			} else if obs.Header.Sync == 0 {
				this.obsList.AddMsm(obs)
				this.bodyCnt++
				this.writePreRinexObsEpoch()
				this.obsList.Reset()
			} else {
				this.obsList.AddMsm(obs)
				this.bodyCnt++
			}

		}
	}

}
